﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    [SerializeField]
    Block itemBlock;
    [SerializeField]
    GridLayoutGroup grid;

    [SerializeField]
    Text switchBtnTxt;

    [SerializeField]
    Text remainder;
    [SerializeField]
    Text time;

    int mineRemainder = 0;
    int curTime = 0;

    enum GameState
    {
        Init,
        Ready2Start,
        InGame,
        Win,
        Lose,
    }
    GameState m_gameState = GameState.Init;
    int totalW = 9;
    int totalH = 9;
    int mineCount = 10;
    Dictionary<Vector2, bool> blockList = new Dictionary<Vector2, bool>();
    Dictionary<Vector2, bool> mineList = new Dictionary<Vector2, bool>();
    Dictionary<Vector2, bool> flagList = new Dictionary<Vector2, bool>();

    List<Block> objList = new List<Block>();

    void Start()
    {
        CreateGame();
    }

    void CreateGame()
    {
        mineRemainder = mineCount;
        curTime = 0;

        remainder.text = mineRemainder.ToString();
        time.text = curTime.ToString();

        m_gameState = GameState.Ready2Start;

        resultPanel.SetActive(false);

        RefreshSwitchBtn();

        CreateMineList();
        RefreshUI();
    }

    void CreateMineList()
    {
        grid.constraintCount = totalW;

        int total = totalH * totalW;
        bool[] tmpList = new bool[total];
        int i = 0;
        for (i = 0; i < total; ++i)
        {
            tmpList[i] = i < mineCount;
        }
        for (i = 0; i < total; ++i)
        {
            int r = Random.Range(0, total);
            var tmp = tmpList[i];
            tmpList[i] = tmpList[r];
            tmpList[r] = tmp;
        }
        mineList.Clear();
        blockList.Clear();
        flagList.Clear();
        for (i = 0; i < total; ++i)
        {
            int x = i % totalW;
            int y = i / totalW;
            Vector2 tmp = new Vector2(x, y);
            if (tmpList[i])
            {
                mineList.Add(tmp, true);
            }
            blockList.Add(tmp, false);
        }
    }

    void RefreshUI()
    {
        int total = totalW * totalH;
        for (int i = 0; i < total; ++i)
        {
            var item = GetItem(i);
            if (item != null)
            {
                item.Show();
                item.Reset(i, OnClickBlock);
            }
        }
    }

    Block GetItem(int _index)
    {
        Block item = null;
        if (_index < objList.Count)
        {
            item = objList[_index];
        }
        else
        {
            item = GameObject.Instantiate(itemBlock, grid.transform);
            objList.Add(item);
        }
        return item;
    }
    [SerializeField]
    bool bUseFlag = false;
    void OnClickBlock(int _index)
    {
        if (m_gameState == GameState.Ready2Start)
        {
            m_gameState = GameState.InGame;
            CancelInvoke("RefreshTime");
            InvokeRepeating("RefreshTime", 0, 1);
        }

        if (m_gameState == GameState.InGame)
        {
            int x = _index % totalW;
            int y = _index / totalW;
            //Debug.Log("OnClickBlock x = " + x + ", y = " + y);
            if (bShowCount)
            {
                ShowCount(x, y);
            }
            if (bUseFlag)
            {
                DoFlagMine(_index);
            }
            else
            {
                DoMineBlock(x, y, false);
            }

            CheckWin();
        }
    }

    void DoFlagMine(int _index)
    {
        int x = _index % totalW;
        int y = _index / totalW;
        var pos = new Vector2(x, y);
        if (x >= 0 && x < totalW
            && y >= 0 && y < totalH
            && !blockList[pos])
        {
            if (flagList.ContainsKey(pos) && flagList[pos])
            {
                flagList.Remove(pos);
                SetBlockState(_index, Block.BlockState.Normal);
                mineRemainder++;
                remainder.text = mineRemainder.ToString();
            }
            else
            {
                if (mineRemainder > 0)
                {
                    flagList.Add(pos, true);
                    SetBlockState(_index, Block.BlockState.Flag);
                    mineRemainder--;
                    remainder.text = mineRemainder.ToString();
                }
            }
        }
    }

    bool DoMineBlock(int x, int y, bool auto)
    {
        //Debug.Log("DoMineBlock x = " + x + ", y = " + y);
        bool bContinue = false;
        var pos = new Vector2(x, y);
        if (x >= 0 && x < totalW
            && y >= 0 && y < totalH
            && !blockList[pos]
            && !(flagList.ContainsKey(pos) && flagList[pos]))
        {
            if (mineList.ContainsKey(pos))
            {
                //Debug.Log("Has Bomb");
                if (!auto)
                {
                    OnLose();
                }
            }
            else
            {
                blockList[pos] = true;
                int tmpIndex = x + y * totalW;
                int n = GetNearMineCount(x, y);
                Block.BlockState tmpState = Block.BlockState.Num;
                if (n == 0)
                {
                    //Debug.Log("Empty x = " + x + ", y = " + y);
                    tmpState = Block.BlockState.Empty;
                    for (int i = 0; i < 3; ++i)
                    {
                        for (int j = 0; j < 3; ++j)
                        {
                            if (i != 1 || j != 1)
                            {
                                DoMineBlock(x - 1 + j, y - 1 + i, true);
                            }
                        }
                    }
                }
                SetBlockState(tmpIndex, tmpState, n);
            }
        }
        return bContinue;
    }

    int GetNearMineCount(int x, int y)
    {
        int n = 0;
        for (int i = 0; i < 3; ++i)
        {
            for (int j = 0; j < 3; ++j)
            {
                int tmpX = x - 1 + j;
                int tmpY = y - 1 + i;
                if ((tmpX != x || tmpY != y)
                    && tmpX >= 0 && tmpX < totalW
                    && tmpY >= 0 && tmpY < totalH)
                {
                    Vector2 tmp = new Vector2(tmpX, tmpY);
                    if (mineList.ContainsKey(tmp))
                    {
                        ++n;
                    }
                }
            }
        }
        return n;
    }

//     void SetBlockState(int _x, int _y, Block.BlockState _state, int _n = 0)
//     {
//         Debug.LogError("set block state x = " + _x + ", y = " + _y + ", state = " + _state);
//         int tmpIndex = _x + _y * totalW;
//         SetBlockState(tmpIndex, _state, _n);
//     }
    void SetBlockState(int _index, Block.BlockState _state, int _n = 0)
    {
        int x = _index % totalW;
        int y = _index / totalW;
        //Debug.LogError("set block state x = " + x + ", y = " + y + ", state = " + _state + " , n = " + _n);
        objList[_index].SetState(_state, _n);
    }


    [SerializeField]
    bool bShowCount = false;
    void ShowCount(int _x, int _y)
    {
        int n = GetNearMineCount(_x, _y);
        Debug.Log("Show Count x = " + _x + ", y = " + _y + ", n = " + n);
    }

    void CheckWin()
    {
        if (m_gameState == GameState.InGame)
        {
            int left = 0;
            foreach (var kv in blockList)
            {
                if (!kv.Value)
                {
                    left++;
                }
            }
            if (left == mineRemainder + flagList.Count)
            {
                OnWin();
            }
        }
    }

    void RefreshTime()
    {
        curTime++;
        time.text = curTime.ToString();
    }

    public void OnSwitch()
    {
        bUseFlag =!bUseFlag;
        RefreshSwitchBtn();
    }
    void RefreshSwitchBtn()
    {
        if (bUseFlag)
        {
            switchBtnTxt.text = "Switch Dig";
        }
        else
        {
            switchBtnTxt.text = "Switch Flag";
        }
    }

    [SerializeField]
    GameObject resultPanel;
    [SerializeField]
    Text resultTxt;

    void OnWin()
    {
        m_gameState = GameState.Win;
        Debug.Log("Win");
        CancelInvoke("RefreshTime");

        resultPanel.SetActive(true);
        resultTxt.text = "You Win!";
    }

    void OnLose()
    {
        // bomb
        Debug.LogError("Bomb Lose");
        m_gameState = GameState.Lose;

        CancelInvoke("RefreshTime");

        resultPanel.SetActive(true);
        resultTxt.text = "You Lose!";
    }

    public void OnRestart()
    {
        CreateGame();
    }
}
